package covertjava.protect;

import java.net.URL;
import java.io.*;
import java.util.ArrayList;
import java.util.StringTokenizer;
import java.security.*;
import sun.misc.BASE64Encoder;

/**
 * <p>Implements application distribution files protection using a checksum</p>
 * <p>Copyright: Copyright (c) 2004 Sams Publishing</p>
 * @author Alex Kalinovsky
 * @version 1.0
 */
public class IntegrityProtector
{
    public static final String FAILED_TO_GET_URL = "IP1";
    public static final String UNEXPECTED_JAR = "IP2";
    public static final String BOOT_CLASSLOADER = "IP4";

    public IntegrityProtector() {
    }


    public String[] readFilePathsFromFile(String filePath) throws Exception {
        InputStream stream = new FileInputStream(filePath);
        byte[] bytes = new byte[stream.available()];
        stream.read(bytes, 0, bytes.length);
        stream.close();
        String pathsString = new String(bytes);
        return readFilePathsFromString(pathsString);
    }


    public String[] readFilePathsFromResource(String resourceName) throws Exception {
        InputStream stream = this.getClass().getResourceAsStream(resourceName);
        byte[] bytes = new byte[stream.available()];
        stream.read(bytes, 0, bytes.length);
        stream.close();
        String pathsString = new String(bytes);
        return readFilePathsFromString(pathsString);
    }


    public String[] readFilePathsFromString(String pathsString) throws Exception {
        ArrayList pathsArray = new ArrayList();
        StringTokenizer tok = new StringTokenizer(pathsString, "\r\n");
        while (tok.hasMoreTokens())
            pathsArray.add(tok.nextToken());
        String[] paths = new String[pathsArray.size()];
        pathsArray.toArray(paths);

        return paths;
    }


    public void assertFilesIntegrity(String[] paths, char separator, String installPath, String checkSum) throws Exception {
        String installCheckSum = getFilesCheckSum(paths, separator, installPath);
        if (installCheckSum.equals(checkSum) == false)
            throw new InternalError("Some of the installation files are corrupt");
    }


    public String getFilesCheckSum(String[] paths, char separator, String installPath) throws Exception {
        long totalSize = 0;
        for (int i = 0; i < paths.length; i++) {
            String path = paths[i];
            if (separator != File.separatorChar)
                path = path.replace(separator, File.separatorChar);
            path = installPath + File.separatorChar + path;
            totalSize += new File(path).length();
        }

        byte[] checkSum = toByteArray(totalSize);
        MessageDigest sha = MessageDigest.getInstance("SHA-1");
        checkSum = sha.digest(checkSum);
        BASE64Encoder encoder = new BASE64Encoder();
        return encoder.encode(checkSum);
    }


    public void assertClassSource(Class cls, String jarName) {
        // Class loader should not be null
        if (cls.getClassLoader() == null)
            throw new InternalError(BOOT_CLASSLOADER);

        String name = cls.getName();
        int lastDot = name.lastIndexOf('.');
        if (lastDot != -1)
            name = name.substring(lastDot + 1);
        URL url = cls.getResource(name + ".class");
        if (url == null)
            throw new InternalError(FAILED_TO_GET_URL);

        name = url.toString();
        if (name.startsWith("jar:") == false || name.indexOf(jarName + "!") == -1)
            throw new InternalError(UNEXPECTED_JAR);
    }


    public byte[] toByteArray(long value) {
        byte[] bytes = new byte[8];
        for(int i=0; i < bytes.length; i++){
            bytes[i] = (byte)(value & 0xFF);
            value = value >>> 8;
        }
        return bytes;
    }


    public static void main(String[] args) throws Exception {
        if (args.length != 1) {
            System.out.println("Syntax: IntegrityProtector [-Dhome=<path to home>] <file list path>");
            System.exit(1);
        }

        IntegrityProtector protector = new IntegrityProtector();
        String[] paths = protector.readFilePathsFromFile(args[0]);

        String homePath = System.getProperty("home", "..");
        String checksum = protector.getFilesCheckSum(paths, '\\', homePath);
        System.out.println("Checksum = [" + checksum + "]");
    }

}
